package com.example.chess.api;

import com.example.chess.game.OnlineUserManager;
import com.example.chess.game.Room;
import com.example.chess.game.RoomManager;
import com.example.chess.mapper.UserMapper;
import com.example.chess.model.User;
import com.example.chess.util.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.io.IOException;

@Component
public class GameAPI extends TextWebSocketHandler {

    ObjectMapper objectMapper = new ObjectMapper();

    @Resource
    private RoomManager roomManager;

    @Resource
    private OnlineUserManager onlineUserManager;

    @Resource
    private UserMapper userMapper;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        GameReadyResponse response = new GameReadyResponse();
        // 获取用户的身份信息
        User user = (User)session.getAttributes().get("user");
        if (user == null){
            response.setOk(false);
            response.setReason("未登录!");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
            return;
        }
        // 判定当前用户是否有匹配到的房间 / 要观战的房间
        Room room = roomManager.getRoomByUser(user.getUserId());
        if (room == null){
            // 当前用户没有匹配的房间
            // 看是否有观战的房间
            String roomToWatch = roomManager.getRoomToWatch(user.getUserId());
            if (roomToWatch == null){
                response.setOk(false);
                response.setReason("用户未匹配!");
                session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
                return;
            }else {
                // 有要观战的房间  --- 将该房间的信息发送回去
                room = roomManager.getRoomById(roomToWatch);
                // 添加进观战队列
                room.enterWatch(user.getUserId(), session);

                System.out.println("要观战的房间号为: " + roomToWatch);
                WatchResponse watchResponse = new WatchResponse();
                watchResponse.setWhiteUser(room.getWhiteUser());
                watchResponse.setBoard(room.getBoard());
                watchResponse.setMessage("watch");
                watchResponse.setState(ResState.SUCCESS);
                session.sendMessage(new TextMessage(objectMapper.writeValueAsString(watchResponse)));
            }
            return;
        }
        // 判定当前用户是否已经在其他地方登录
        if (onlineUserManager.getRoomSession(user.getUserId()) != null ||
                onlineUserManager.getSession(user.getUserId()) != null){
            // 多地登录
            response.setOk(false);
            response.setReason("已在别地登录!");
            response.setMessage("repeatConnection");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
            return;
        }
        // 将玩家添加到进入房间队列
        onlineUserManager.enterRoom(user.getUserId(), session);

        // 把玩家加入到匹配到的房间
        synchronized (room){
            if (room.getUser1() == null){
                room.setUser1(user);
                room.setWhiteUser(user.getUserId());
                System.out.println("玩家 " + user.getNickname() + " 准备就绪, 作为玩家1");
                return;
            }
            if (room.getUser2() == null){
                room.setUser2(user);
                System.out.println("玩家 " + user.getNickname() + " 准备就绪, 作为玩家2");
                // 返回双方都准备就绪的通知
                // 通知玩家一
                noticeGameReady(room, room.getUser1(), room.getUser2());
                // 通知玩家 2
                noticeGameReady(room, room.getUser2(), room.getUser1());
                return;
            }
        }
        // 再有玩家进来, 则会报错
        response.setOk(false);
        response.setReason("当前房间已满!");
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
    }

    private void noticeGameReady(Room room, User thisUser, User thatUser) throws IOException {
        GameReadyResponse response = new GameReadyResponse();
        // 通知玩家一
        response.setOk(true);
        response.setMessage("gameReady");
        response.setRoomId(room.getRoomId());
        response.setThisUserId(thisUser.getUserId());
        response.setThatUserId(thatUser.getUserId());
        response.setWhiteUser(room.getWhiteUser());

        // 通知玩家
        WebSocketSession session = onlineUserManager.getRoomSession(thisUser.getUserId());
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
    }


    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        // 落子响应
        User user = (User)session.getAttributes().get("user");
        if (user == null){
            System.out.println("玩家未登录!");
            return;
        }
        GameRequest request = objectMapper.readValue(message.getPayload(), GameRequest.class);
        if (request.getRoomId() != null){
            // 是观战的用户   --  将现在的棋局形式发过去
            Room room = roomManager.getRoomById(request.getRoomId());
            System.out.println("要观战的房间号为: " + request.getRoomId());
            WatchResponse watchResponse = new WatchResponse();
            watchResponse.setWhiteUser(room.getWhiteUser());
            watchResponse.setBoard(room.getBoard());
            watchResponse.setMessage("watch");// 初始化
            watchResponse.setState(ResState.SUCCESS);
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(watchResponse)));
        }else {
            // 根据玩家 ID获取房间对象
            Room room = roomManager.getRoomByUser(user.getUserId());
            // 通过 room对象来处理请求
            room.putChess(message.getPayload());
        }
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        User user = (User)session.getAttributes().get("user");
        if (user == null){
            // 用户下线了
            return;
        }
        WebSocketSession roomSession = onlineUserManager.getRoomSession(user.getUserId());
        if (roomSession != null && roomSession == session){
            // 当前用户在线   ---  下线
            onlineUserManager.outRoom(user.getUserId());
        }
        System.out.println("当前用户" + user.getNickname() + "游戏房间连接异常");
        // 通知对手该方掉线
        noticeThatUserWin(user);
    }

    private void noticeThatUserWin(User user) throws IOException {
        // 找到当前房间
        Room room = roomManager.getRoomByUser(user.getUserId());
        if (room == null){
            return;
        }
        // 找到对方玩家
        User thatUser = (room.getUser1() == user) ? room.getUser2() : room.getUser1();
        // 给对手发信息
        WebSocketSession session = onlineUserManager.getRoomSession(thatUser.getUserId());
        if (session == null){
            return;
        }

        GameResponse response = new GameResponse();
        response.setReason("dropped");
        response.setUserId(thatUser.getUserId());
        response.setWinner(thatUser.getUserId());
        response.setMessage("putChess");
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));

        // 更新用户积分
        int winId = thatUser.getUserId();
        int loseId = user.getUserId();
        userMapper.winByDrop(winId);
        userMapper.lose(loseId);

        // 释放房间对象
        roomManager.remove(room.getRoomId(), room.getUser1().getUserId(), room.getUser2().getUserId());
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        User user = (User)session.getAttributes().get("user");
        if (user == null){
            // 用户下线了
            return;
        }
        WebSocketSession roomSession = onlineUserManager.getRoomSession(user.getUserId());
        if (roomSession != null && roomSession == session){
            // 当前用户在线   ---  下线
            onlineUserManager.outRoom(user.getUserId());
        }
        System.out.println("当前用户" + user.getNickname() + "离开游戏房间");
        // 通知对手该方掉线
        noticeThatUserWin(user);
    }
}
